home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / MiscFindPanel.bproj / MiscFindPanel.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  16.2 KB  |  660 lines

  1. /*
  2.  *  Copyright (c) 1993 Christopher J. Kane.  All rights reserved.
  3.  *
  4.  *  This software is subject to the terms of the MiscKit license
  5.  *  agreement.  Refer to the license document included with the
  6.  *  MiscKit distribution for these terms.
  7.  *
  8.  *  Version: 1.1.1 (14 December 1993)
  9.  *
  10.  *  Incorporates a bug-fix by Annard Brouwer <annard@stack.urc.tue.nl>.
  11.  *  The nextText outlet of the findTextField was remaining set to the
  12.  *  replaceTextField.  When the replace controls were hidden, this
  13.  *  caused the Tab key in the findTextField to appear to do nothing.
  14.  *  The text in the field is now correctly selected when the Tab key
  15.  *  is pressed.
  16.  */
  17.  
  18. #import "MiscFindPanel.h"
  19. #import <misckit/SearchableText.h>
  20. #import "Composer.h"
  21. #import "MainWindowControl.h"
  22. #import "descriptors.h"
  23. #import <misckit/MiscAppDefaults.h>
  24.  
  25. #define DURING        NX_DURING
  26. #define HANDLER        NX_HANDLER switch (NXLocalHandler.code) {
  27. #define ENDHANDLER    default: NXLogError("Uncaught exception: %d," \
  28.             " in %s:%s.\n", NXLocalHandler.code, __FILE__, \
  29.             sel_getName(_cmd)); abort();} NX_ENDHANDLER
  30. #define IGNORE(E)    case E: NXLogError("%s exception in %s:%s. " \
  31.             "Ignored.\n", #E, __FILE__, sel_getName(_cmd)); break
  32.  
  33. #define LocalString(K)    NXLoadLocalizedStringFromTableInBundle(NULL, \
  34.             [NXBundle bundleForClass:[self class]], K, "")
  35.  
  36. static MiscFindPanel *_sharedFindPanel=nil;
  37. static Pasteboard *_findPb=nil;
  38. static Object *_firstConformer=nil;
  39. static int _pbChangeCount=0;
  40. static BOOL _replEnabled=YES;
  41. static BOOL _useFindPb=YES;
  42. static BOOL _allocationOK=NO;
  43. static BOOL _resizeOK=NO;
  44. static BOOL _fpLoaded=NO;
  45.  
  46. #define GSCOPE_TEXT 0
  47. #define GSCOPE_SUBJECT 1
  48. #define GSCOPE_MSGID 2
  49. #define GSCOPE_NEWSGROUPS 3
  50. #define GSCOPE_AUTHOR 4
  51. #define GSCOPE_REFERENCES 5
  52.  
  53. @interface otherMethods
  54.  
  55. - newsgroupMatrix;
  56. - articleMatrix;
  57. - mySet;
  58. - (int)grep:(const char *)pattern regexpr:(BOOL)regexpr cases:(BOOL)cases;
  59.  
  60. @end
  61.  
  62. @implementation MiscFindPanel
  63.  
  64.  
  65. - _calcFindPanelTarget
  66. {
  67.    id d,r=nil;
  68.    id i=[NXApp keyWindow];
  69.    if(i==self)
  70.       i=[NXApp mainWindow];
  71.    
  72.    if((i==nil)||(i==self)) return nil;
  73.    d=[i delegate];
  74.    if(d==nil) return nil;
  75.  
  76.    if([d isKindOf:[MainWindowControl class]]){
  77.       int scope=[[scopeSelection selectedCell] tag];
  78.       if(scope==GSCOPE_TEXT) 
  79.          r=[d articleText];
  80.       else if(scope==GSCOPE_NEWSGROUPS)
  81.          r=[d newsgroupMatrix];
  82.       else
  83.          r=[d articleMatrix];
  84.    }
  85.    else if([d isKindOf:[Composer class]])
  86.       r=[d theText];
  87.  
  88.    if([r isKindOf:[Text class]]){
  89.       NXSelPt start,end;
  90.  
  91.       [r getSel:&start :&end];
  92.       if(start.cp==-1)
  93.          [r setSel:0 :0];
  94.    }
  95.  
  96.    return r;
  97. }
  98.  
  99. /*
  100.   id i;
  101.   if (_firstConformer!=nil)
  102.     return _firstConformer;
  103.   if (self!=[NXApp keyWindow])
  104.     {
  105.       for(i=[[NXApp keyWindow] firstResponder]; i; i = [i nextResponder])
  106.         if ([i conformsTo:@protocol(SearchableText)])
  107.           return i;
  108.       i = [[NXApp keyWindow] delegate];
  109.       if ([i conformsTo:@protocol(SearchableText)])
  110.         return i;
  111.     }
  112.   if ([NXApp keyWindow]!=[NXApp mainWindow])
  113.     {
  114.       for(i=[[NXApp mainWindow] firstResponder]; i; i = [i nextResponder])
  115.         if ([i conformsTo:@protocol(SearchableText)])
  116.           return i;
  117.       i = [[NXApp mainWindow] delegate];
  118.       if ([i conformsTo:@protocol(SearchableText)])
  119.         return i;
  120.     }
  121.   i = NXApp;
  122.   if ([i conformsTo:@protocol(SearchableText)])
  123.     return i;
  124.   i = [NXApp delegate];
  125.   if ([i conformsTo:@protocol(SearchableText)])
  126.     return i;
  127.   return nil;
  128. }
  129. */
  130.  
  131.  
  132. - (void)_getFindPbData
  133. {
  134.   if (_pbChangeCount==[_findPb changeCount])
  135.     return;
  136.   DURING
  137.     if ([_findPb findAvailableTypeFrom:&NXAsciiPboardType num:1])
  138.       {
  139.         char *text;
  140.         int len;
  141.         if ([_findPb readType:NXAsciiPboardType data:&text length:&len])
  142.           {
  143.             [findTextField setStringValue:text];
  144.             [_findPb deallocatePasteboardData:text length:len];
  145.           }
  146.       }
  147.   HANDLER
  148.     IGNORE(NX_pasteboardComm);
  149.     IGNORE(NX_appkitVMError);
  150.   ENDHANDLER
  151.   _pbChangeCount = [_findPb changeCount];
  152. }
  153.  
  154. - (void)_modifyFindPanel
  155. {
  156.   NXRect r;
  157.   [contentView getBounds:&r];
  158.   [self disableFlushWindow];
  159.   if (_replEnabled)
  160.     {
  161.       _resizeOK = YES;
  162.       [super sizeWindow:NX_WIDTH(&r) :NX_HEIGHT(&r)+25.0];
  163.       _resizeOK = NO;
  164.       [[[findTextField superview] superview] moveBy:0.0 :25.0];
  165.       [contentView addSubview:_replBox :NX_BELOW relativeTo:nil];
  166.       [findTextField setNextText:replaceTextField];    /* added by Annard */
  167.     }
  168.   else
  169.     {
  170.       [_replBox removeFromSuperview];
  171.       [[[findTextField superview] superview] moveBy:0.0 :-25.0];
  172.       _resizeOK = YES;
  173.       [super sizeWindow:NX_WIDTH(&r) :NX_HEIGHT(&r)-25.0];
  174.       _resizeOK = NO;
  175.       [findTextField setNextText:findTextField];    /* added by Annard */
  176.     }
  177.   [self display];
  178.   [[self reenableFlushWindow] flushWindowIfNeeded];
  179. }
  180.  
  181. - (void)_resetFindPanel
  182. {
  183.   [messageTextField setStringValue:""];
  184.   [findTextField selectText:nil];
  185. }
  186.  
  187. - (void)_setFindPbData
  188. {
  189.   const char *text;
  190.   if (_pbChangeCount==[_findPb changeCount])
  191.     return;
  192.   text = [findTextField stringValue];
  193.   if (*text=='\0')
  194.     return;
  195.   DURING
  196.     _pbChangeCount = [_findPb declareTypes:&NXAsciiPboardType num:1 owner:NULL];
  197.     [_findPb writeType:NXAsciiPboardType data:text length:(int)strlen(text)];
  198.   HANDLER
  199.     IGNORE(NX_pasteboardComm);
  200.   ENDHANDLER
  201. }
  202.  
  203. + setFindPbEnabled:(BOOL)aBool
  204. {
  205.   _useFindPb = aBool;
  206.   return self;
  207. }
  208.  
  209. + setFirstConformer:aConformer
  210. {
  211.   id formerConformer = _firstConformer;
  212.   if (aConformer==nil || [aConformer conformsTo:@protocol(SearchableText)])
  213.     _firstConformer = aConformer;
  214.   return formerConformer;
  215. }
  216.  
  217. + setReplacementEnabled:(BOOL)aBool
  218. {
  219.   if (aBool!=_replEnabled)
  220.     {
  221.       _replEnabled = aBool;
  222.       [_sharedFindPanel _modifyFindPanel];
  223.     }
  224.   return self;
  225. }
  226.  
  227. + sharedInstance
  228. {
  229.   if (_sharedFindPanel==nil && !_fpLoaded)
  230.     {
  231.       char path[MAXPATHLEN+16];
  232.       _fpLoaded = YES;
  233.       _findPb = [Pasteboard newName:NXFindPboard];
  234.       _allocationOK = YES;
  235.       if ([[NXBundle bundleForClass:self] getPath:path forResource:"FindPanel.nib" ofType:NULL])
  236.         _sharedFindPanel = [NXApp loadNibFile:path owner:NXApp withNames:NO];
  237.       _allocationOK = NO;
  238.       if (_sharedFindPanel==nil)
  239.         NXRunAlertPanel(LocalString("LOAD_ERR"), LocalString("LOAD_ERR_MSG"), NULL, NULL, NULL);
  240.       else
  241.         {
  242.           if (!_replEnabled)
  243.             [_sharedFindPanel _modifyFindPanel];
  244.           [_sharedFindPanel setExcludedFromWindowsMenu:YES];
  245.           [_sharedFindPanel useOptimizedDrawing:YES];
  246.         }
  247.     }
  248.   return _sharedFindPanel;
  249. }
  250.  
  251. - enterSelection:sender
  252. {
  253.   [[self _calcFindPanelTarget] writeSelectionToPasteboard:_findPb asType:NXAsciiPboardType];
  254.   [self _getFindPbData];
  255.   [self _resetFindPanel];
  256.   return self;
  257. }
  258.  
  259. - findBackward:sender
  260. {
  261.   [self _resetFindPanel];
  262.   if (*[findTextField stringValue]=='\0' && _useFindPb)
  263.     [self _getFindPbData];
  264.   if (*[findTextField stringValue]=='\0')
  265.     [self makeKeyAndOrderFront:nil];
  266.   else
  267.     {
  268.       int pos, size, result;
  269.       id target = [self _calcFindPanelTarget];
  270.       if (_useFindPb)
  271.         [self _setFindPbData];
  272.       result = [target searchFor:[findTextField stringValue] mode:SelStartToSelEnd reverse:YES regexpr:[regExprButton state] cases:![ignoreCaseButton state] position:&pos size:&size];
  273.       if (target==nil || result<0)
  274.         {
  275.           [messageTextField setStringValue:LocalString("INVALID_OP")];
  276.           NXBeep();
  277.         }
  278.       else if (result==0)
  279.         {
  280.           [messageTextField setStringValue:LocalString("NOT_FOUND")];
  281.           NXBeep();
  282.         }
  283.       else
  284.         {
  285.           [target selectTextFrom:pos to:pos+size];
  286.           [target makeSelectionVisible];
  287.         }
  288.     }
  289.   return self;
  290. }
  291.  
  292. - findForward:sender
  293. {
  294.   [self _resetFindPanel];
  295.   if (*[findTextField stringValue]=='\0' && _useFindPb)
  296.     [self _getFindPbData];
  297.   if (*[findTextField stringValue]=='\0')
  298.     [self makeKeyAndOrderFront:nil];
  299.   else
  300.     {
  301.       int pos, size, result;
  302.       id target = [self _calcFindPanelTarget];
  303.       if (_useFindPb)
  304.         [self _setFindPbData];
  305.       result = [target searchFor:[findTextField stringValue] mode:SelEndToSelStart reverse:NO regexpr:[regExprButton state] cases:![ignoreCaseButton state] position:&pos size:&size];
  306.       if (target==nil || result<0)
  307.         {
  308.           [messageTextField setStringValue:LocalString("INVALID_OP")];
  309.           NXBeep();
  310.         }
  311.       else if (result==0)
  312.         {
  313.           [messageTextField setStringValue:LocalString("NOT_FOUND")];
  314.           NXBeep();
  315.         }
  316.       else
  317.         {
  318.           [target selectTextFrom:pos to:pos+size];
  319.           [target makeSelectionVisible];
  320.         }
  321.     }
  322.   return self;
  323. }
  324.  
  325. - jumpToSelection:sender
  326. {
  327.   [[self _calcFindPanelTarget] makeSelectionVisible];
  328.   return self;
  329. }
  330.  
  331. - replace:sender
  332. {
  333.   [self _resetFindPanel];
  334.   [[self _calcFindPanelTarget] replaceSelection:[replaceTextField stringValue]];
  335.   return self;
  336. }
  337.  
  338. - replaceAll:sender
  339. {
  340.   id target;
  341.   int count;
  342.   [self _resetFindPanel];
  343.   if (*[findTextField stringValue]=='\0')
  344.     return self;
  345.   target = [self _calcFindPanelTarget];
  346.   count = [target replaceAll:[findTextField stringValue] with:[replaceTextField stringValue] mode:([scopeMatrix selectedRow]?SelStartToSelEnd:TextEdgeToTextEdge) regexpr:[regExprButton state] cases:![ignoreCaseButton state]];
  347.   if (count<0 || target==nil)
  348.     {
  349.       [messageTextField setStringValue:LocalString("INVALID_OP")];
  350.       NXBeep();
  351.     }
  352.   else
  353.     {
  354.       char buffer[256];
  355.       sprintf(buffer, LocalString("N_REPLACED"), count);
  356.       [messageTextField setStringValue:buffer];
  357.       [target makeSelectionVisible];
  358.     }
  359.   return self;
  360. }
  361.  
  362. - replaceAndFind:sender
  363. {
  364.   [[self replace:sender] findForward:sender];
  365.   return self;
  366. }
  367.  
  368. - grep:sender
  369. {
  370.   [self _resetFindPanel];
  371.   if (*[findTextField stringValue]=='\0' && _useFindPb)
  372.     [self _getFindPbData];
  373.   if (*[findTextField stringValue]=='\0')
  374.     [self makeKeyAndOrderFront:nil];
  375.   else
  376.     {
  377.       int result;
  378.       id target = [self _calcFindPanelTarget];
  379.       if (_useFindPb)
  380.         [self _setFindPbData];
  381.       if((![target respondsTo:@selector(mySet)])||
  382.          (![[target mySet] respondsTo:@selector(grep:regexpr:cases:)]))
  383.          target=nil;
  384.       result = [[target mySet] grep:[findTextField stringValue] regexpr:[regExprButton state] cases:![ignoreCaseButton state]];
  385.       if (target==nil || result<0)
  386.         {
  387.           [messageTextField setStringValue:LocalString("INVALID_OP")];
  388.           NXBeep();
  389.         }
  390.       else if (result==0)
  391.         {
  392.           [messageTextField setStringValue:LocalString("NOT_FOUND")];
  393.           NXBeep();
  394.         }
  395.     }
  396.   return self;
  397. }
  398.  
  399. - gscopeChanged:sender
  400. {
  401.    int sel=[[scopeSelection selectedCell] tag];
  402.    int newDefault=-1;
  403.  
  404.    switch(sel){
  405.       case GSCOPE_SUBJECT:
  406.          newDefault=SUBJECT;
  407.          break;
  408.       case GSCOPE_MSGID:
  409.          newDefault=MSG_ID;
  410.          break;
  411.       case GSCOPE_AUTHOR:
  412.          newDefault=FROM;
  413.          break;
  414.       case GSCOPE_REFERENCES:
  415.          newDefault=REFS;
  416.          break;
  417.    }
  418.  
  419.    if(newDefault>=0)
  420.       [NXApp setDefault:"findGeneralScope" toInt:newDefault];
  421.  
  422.     [replButton1 setEnabled:(sel==GSCOPE_TEXT)];
  423.     [replButton2 setEnabled:(sel==GSCOPE_TEXT)];
  424.     [replButton3 setEnabled:(sel==GSCOPE_TEXT)];
  425.    [replButton4 setEnabled:(sel!=GSCOPE_TEXT)];      
  426.  
  427.    return self;
  428. }
  429.  
  430. - (TextField *)findTextField
  431. {
  432.   return findTextField;
  433. }
  434.  
  435. - (Button *)ignoreCaseButton
  436. {
  437.   return ignoreCaseButton;
  438. }
  439.  
  440. - (TextField *)messageTextField
  441. {
  442.   return messageTextField;
  443. }
  444.  
  445. - (Button *)regExprButton
  446. {
  447.   return regExprButton;
  448. }
  449.  
  450. - (TextField *)replaceTextField
  451. {
  452.   return replaceTextField;
  453. }
  454.  
  455. - (Matrix *)scopeMatrix
  456. {
  457.   return scopeMatrix;
  458. }
  459.  
  460. - (Matrix *)scopeSelection
  461. {
  462.    return scopeSelection;
  463. }
  464.  
  465. - textDidEnd:sender endChar:(unsigned short)theChar
  466. {
  467.   if (theChar==NX_RETURN)
  468.     [self orderOut:nil];
  469.   return self;
  470. }
  471.  
  472. - (BOOL)textWillChange:sender
  473. {
  474.   return NO;
  475. }
  476.  
  477. - (BOOL)textWillEnd:sender
  478. {
  479.   return NO;
  480. }
  481.  
  482. + (BOOL)_canAlloc
  483. {
  484.   return _allocationOK;
  485. }
  486.  
  487. + alloc
  488. {
  489.   if (!_allocationOK)
  490.     return [self doesNotRecognize:_cmd];
  491.   return class_createInstanceFromZone(self, 0, NXDefaultMallocZone());
  492. }
  493.  
  494. + allocFromZone:(NXZone *)zone
  495. {
  496.   if (!_allocationOK)
  497.     return [self doesNotRecognize:_cmd];
  498.   if (zone!=NX_NOZONE)
  499.     return class_createInstanceFromZone(self, 0, zone);
  500.   return nil;
  501. }
  502.  
  503. + (BOOL)instancesRespondTo:(SEL)aSel
  504. {
  505.   if (aSel==@selector(copy) ||
  506.       aSel==@selector(copyFromZone:) ||
  507.       aSel==@selector(free) ||
  508.       aSel==@selector(init) ||
  509.       aSel==@selector(initContent:style:backing:buttonMask:defer:) ||
  510.       aSel==@selector(initContent:style:backing:buttonMask:defer:screen:) ||
  511.       aSel==@selector(miniaturize:) ||
  512.       aSel==@selector(placeWindow:) ||
  513.       aSel==@selector(placeWindow:screen:) ||
  514.       aSel==@selector(placeWindowAndDisplay:) ||
  515.       aSel==@selector(setDocEdited:) ||
  516.       aSel==@selector(sizeWindow::))
  517.     return NO;
  518.   return [super instancesRespondTo:aSel];
  519. }
  520.  
  521. + new
  522. {
  523.   return [self doesNotRecognize:_cmd];
  524. }
  525.  
  526. + newContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag
  527. {
  528.   return [self doesNotRecognize:_cmd];
  529. }
  530.  
  531. + newContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag screen:(const NXScreen *)screen
  532. {
  533.   return [self doesNotRecognize:_cmd];
  534. }
  535.  
  536. - copyFromZone:(NXZone *)zone
  537. {
  538.   return self;
  539. }
  540.  
  541. - free
  542. {
  543.   return self;
  544. }
  545.  
  546. - init
  547. {
  548.   return [self doesNotRecognize:_cmd];
  549. }
  550.  
  551. - initContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag
  552. {
  553.   if (!_allocationOK)
  554.     return [self doesNotRecognize:_cmd];
  555.   return [super initContent:contentRect style:aStyle backing:bufferingType buttonMask:mask defer:flag];
  556. }
  557.  
  558. - initContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag screen:(const NXScreen *)screen
  559. {
  560.   return [self doesNotRecognize:_cmd];
  561. }
  562.  
  563. - miniaturize:sender
  564. {
  565.   return self;
  566. }
  567.  
  568. - orderWindow:(int)place relativeTo:(int)otherWin
  569. {
  570.   if (place!=NX_OUT)
  571.     {
  572.       if (_useFindPb)
  573.         [self _getFindPbData];
  574.       [self _resetFindPanel];
  575.     }
  576.   return [super orderWindow:place relativeTo:otherWin];
  577. }
  578.  
  579. - placeWindow:(const NXRect *)aRect
  580. {
  581.   if (_resizeOK)
  582.     return [super placeWindow:aRect];
  583.   return [self moveTo:NX_X(aRect) :NX_Y(aRect)];
  584. }
  585.  
  586. - placeWindow:(const NXRect *)aRect screen:(const NXScreen *)aScreen
  587. {
  588.   if (_resizeOK)
  589.     return [super placeWindow:aRect screen:aScreen];
  590.   return [self moveTo:NX_X(aRect) :NX_Y(aRect) screen:aScreen];
  591. }
  592.  
  593. - placeWindowAndDisplay:(const NXRect *)aRect
  594. {
  595.   if (_resizeOK)
  596.     return [super placeWindowAndDisplay:aRect];
  597.   return [[self moveTo:NX_X(aRect) :NX_Y(aRect)] display];
  598. }
  599.  
  600. - read:(NXTypedStream *)stream
  601. {
  602.   [super read:stream];
  603.   findTextField = NXReadObject(stream);
  604.   replaceTextField = NXReadObject(stream);
  605.   messageTextField = NXReadObject(stream);
  606.   ignoreCaseButton = NXReadObject(stream);
  607.   regExprButton = NXReadObject(stream);
  608.   scopeMatrix = NXReadObject(stream);
  609.   _replBox = NXReadObject(stream);
  610.   return self;
  611. }
  612.  
  613. - (BOOL)respondsTo:(SEL)aSel
  614. {
  615.   if (aSel==@selector(alloc) ||
  616.       aSel==@selector(allocFromZone:) ||
  617.       aSel==@selector(new) ||
  618.       aSel==@selector(newContent:style:backing:buttonMask:defer:) ||
  619.       aSel==@selector(newContent:style:backing:buttonMask:defer:screen:) ||
  620.       aSel==@selector(copy) ||
  621.       aSel==@selector(copyFromZone:) ||
  622.       aSel==@selector(free) ||
  623.       aSel==@selector(init) ||
  624.       aSel==@selector(initContent:style:backing:buttonMask:defer:) ||
  625.       aSel==@selector(initContent:style:backing:buttonMask:defer:screen:) ||
  626.       aSel==@selector(miniaturize:) ||
  627.       aSel==@selector(placeWindow:) ||
  628.       aSel==@selector(placeWindow:screen:) ||
  629.       aSel==@selector(placeWindowAndDisplay:) ||
  630.       aSel==@selector(setDocEdited:) ||
  631.       aSel==@selector(sizeWindow::))
  632.     return NO;
  633.   return [super respondsTo:aSel];
  634. }
  635.  
  636. - setDocEdited:(BOOL)aBool
  637. {
  638.   return self;
  639. }
  640.  
  641. - sizeWindow:(float)x :(float)y
  642. {
  643.   return self;
  644. }
  645.  
  646. - write:(NXTypedStream *)stream
  647. {
  648.   [super write:stream];
  649.   NXWriteObjectReference(stream, findTextField);
  650.   NXWriteObjectReference(stream, replaceTextField);
  651.   NXWriteObjectReference(stream, messageTextField);
  652.   NXWriteObjectReference(stream, ignoreCaseButton);
  653.   NXWriteObjectReference(stream, regExprButton);
  654.   NXWriteObjectReference(stream, scopeMatrix);
  655.   NXWriteObjectReference(stream, _replBox);
  656.   return self;
  657. }
  658.  
  659. @end
  660.